/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * java.lang.VMThread
 */
#include "../Dalvik.h"
#include "../native/InternalNativePriv.h"


/*
 * static void create(Thread t, long stacksize)
 *
 * This is eventually called as a result of Thread.start().
 *
 * Throws an exception on failure.
 */
static void Dalvik_java_lang_VMThread_create(const u4 *args, JValue *pResult) {
    Object *threadObj = (Object *) args[0];
    s8 stackSize = GET_ARG_LONG(args, 1);

    /* copying collector will pin threadObj for us since it was an argument */
    dvmCreateInterpThread(threadObj, (int) stackSize);
    RETURN_VOID();
}

/*
 * static Thread currentThread()
 */
static void Dalvik_java_lang_VMThread_currentThread(const u4 *args,
                                                    JValue *pResult) {
    UNUSED_PARAMETER(args);

    RETURN_PTR(dvmThreadSelf()->threadObj);
}

/*
 * void getStatus()
 *
 * Gets the Thread status. Result is in VM terms, has to be mapped to
 * Thread.State by interpreted code.
 */
static void Dalvik_java_lang_VMThread_getStatus(const u4 *args, JValue *pResult) {
    Object *thisPtr = (Object *) args[0];
    Thread *thread;
    int result;

    dvmLockThreadList(NULL);
    thread = dvmGetThreadFromThreadObject(thisPtr);
    if (thread != NULL)
        result = thread->status;
    else
        result = THREAD_ZOMBIE;     // assume it used to exist and is now gone
    dvmUnlockThreadList();

    RETURN_INT(result);
}

/*
 * boolean holdsLock(Object object)
 *
 * Returns whether the current thread has a monitor lock on the specific
 * object.
 */
static void Dalvik_java_lang_VMThread_holdsLock(const u4 *args, JValue *pResult) {
    Object *thisPtr = (Object *) args[0];
    Object *object = (Object *) args[1];
    Thread *thread;

    if (object == NULL) {
        dvmThrowNullPointerException("object == null");
        RETURN_VOID();
    }

    dvmLockThreadList(NULL);
    thread = dvmGetThreadFromThreadObject(thisPtr);
    int result = dvmHoldsLock(thread, object);
    dvmUnlockThreadList();

    RETURN_BOOLEAN(result);
}

/*
 * void interrupt()
 *
 * Interrupt a thread that is waiting (or is about to wait) on a monitor.
 */
static void Dalvik_java_lang_VMThread_interrupt(const u4 *args, JValue *pResult) {
    Object *thisPtr = (Object *) args[0];
    Thread *thread;

    dvmLockThreadList(NULL);
    thread = dvmGetThreadFromThreadObject(thisPtr);
    if (thread != NULL)
        dvmThreadInterrupt(thread);
    dvmUnlockThreadList();
    RETURN_VOID();
}

/*
 * static boolean interrupted()
 *
 * Determine if the current thread has been interrupted.  Clears the flag.
 */
static void Dalvik_java_lang_VMThread_interrupted(const u4 *args,
                                                  JValue *pResult) {
    Thread *self = dvmThreadSelf();
    bool interrupted;

    UNUSED_PARAMETER(args);

    interrupted = self->interrupted;
    self->interrupted = false;
    RETURN_BOOLEAN(interrupted);
}

/*
 * boolean isInterrupted()
 *
 * Determine if the specified thread has been interrupted.  Does not clear
 * the flag.
 */
static void Dalvik_java_lang_VMThread_isInterrupted(const u4 *args,
                                                    JValue *pResult) {
    Object *thisPtr = (Object *) args[0];
    Thread *thread;
    bool interrupted;

    dvmLockThreadList(NULL);
    thread = dvmGetThreadFromThreadObject(thisPtr);
    if (thread != NULL)
        interrupted = thread->interrupted;
    else
        interrupted = false;
    dvmUnlockThreadList();

    RETURN_BOOLEAN(interrupted);
}

/*
 * void nameChanged(String newName)
 *
 * The name of the target thread has changed.  We may need to alert DDMS.
 */
static void Dalvik_java_lang_VMThread_nameChanged(const u4 *args,
                                                  JValue *pResult) {
    Object *thisPtr = (Object *) args[0];
    StringObject *nameStr = (StringObject *) args[1];
    Thread *thread;
    int threadId = -1;

    /* get the thread's ID */
    dvmLockThreadList(NULL);
    thread = dvmGetThreadFromThreadObject(thisPtr);
    if (thread != NULL)
        threadId = thread->threadId;
    dvmUnlockThreadList();

    dvmDdmSendThreadNameChange(threadId, nameStr);
    //char* str = dvmCreateCstrFromString(nameStr);
    //ALOGI("UPDATE: threadid=%d now '%s'", threadId, str);
    //free(str);

    RETURN_VOID();
}

/*
 * void setPriority(int newPriority)
 *
 * Alter the priority of the specified thread.  "newPriority" will range
 * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
 * threads at Thread.NORM_PRIORITY (5).
 */
static void Dalvik_java_lang_VMThread_setPriority(const u4 *args,
                                                  JValue *pResult) {
    Object *thisPtr = (Object *) args[0];
    int newPriority = args[1];
    Thread *thread;

    dvmLockThreadList(NULL);
    thread = dvmGetThreadFromThreadObject(thisPtr);
    if (thread != NULL)
        dvmChangeThreadPriority(thread, newPriority);
    //dvmDumpAllThreads(false);
    dvmUnlockThreadList();

    RETURN_VOID();
}

/*
 * static void sleep(long msec, int nsec)
 */
static void Dalvik_java_lang_VMThread_sleep(const u4 *args, JValue *pResult) {
    dvmThreadSleep(GET_ARG_LONG(args, 0), args[2]);
    RETURN_VOID();
}

/*
 * public void yield()
 *
 * Causes the thread to temporarily pause and allow other threads to execute.
 *
 * The exact behavior is poorly defined.  Some discussion here:
 *   http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html
 */
static void Dalvik_java_lang_VMThread_yield(const u4 *args, JValue *pResult) {
    UNUSED_PARAMETER(args);

    sched_yield();

    RETURN_VOID();
}

const DalvikNativeMethod dvm_java_lang_VMThread[] = {
        {"create",        "(Ljava/lang/Thread;J)V",
                Dalvik_java_lang_VMThread_create},
        {"currentThread", "()Ljava/lang/Thread;",
                Dalvik_java_lang_VMThread_currentThread},
        {"getStatus",     "()I",
                Dalvik_java_lang_VMThread_getStatus},
        {"holdsLock",     "(Ljava/lang/Object;)Z",
                Dalvik_java_lang_VMThread_holdsLock},
        {"interrupt",     "()V",
                Dalvik_java_lang_VMThread_interrupt},
        {"interrupted",   "()Z",
                Dalvik_java_lang_VMThread_interrupted},
        {"isInterrupted", "()Z",
                Dalvik_java_lang_VMThread_isInterrupted},
        {"nameChanged",   "(Ljava/lang/String;)V",
                Dalvik_java_lang_VMThread_nameChanged},
        {"setPriority",   "(I)V",
                Dalvik_java_lang_VMThread_setPriority},
        {"sleep",         "(JI)V",
                Dalvik_java_lang_VMThread_sleep},
        {"yield",         "()V",
                Dalvik_java_lang_VMThread_yield},
        {NULL, NULL, NULL},
};
